package com.crawl.proxy;

import com.crawl.core.util.*;
import com.crawl.proxy.entity.Proxy;
import com.crawl.proxy.task.ProxyPageTask;
import com.crawl.core.httpclient.AbstractHttpClient;
import com.crawl.proxy.task.ProxySerializeTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 代理Http客户端
 * 主要功能是http代理页面的下载、解析以及代理的测试等
 */
public class ProxyHttpClient extends AbstractHttpClient {
    private static final Logger logger = LoggerFactory.getLogger(ProxyHttpClient.class);
    private volatile static ProxyHttpClient instance;

    /**
     * 线程安全的单例模式
     * @return
     */
    public static ProxyHttpClient getInstance(){
        if (instance == null){
            synchronized (ProxyHttpClient.class){
                if (instance == null){
                    instance = new ProxyHttpClient();
                }
            }
        }
        return instance;
    }
    /**
     * 代理测试线程池
     */
    private ThreadPoolExecutor proxyTestThreadPool;
    /**
     * 代理页下载线程池
     */
    private ThreadPoolExecutor proxyDownloadThreadPool;
    private ProxyHttpClient(){
        initThreadPool();
        initProxy();
    }
    /**
     * 初始化线程池
     */
    private void initThreadPool(){
        //创建一个corePoolSize为100，maxmumPoolSize为100，任务队列长度为10000的线程池，用于执行代理测试任务
        proxyTestThreadPool = new SimpleThreadPoolExecutor(100, 100,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(10000),
                new ThreadPoolExecutor.DiscardPolicy(),
                "proxyTestThreadPool");
        //创建一个corePoolSize为10，maximumPoolSize为10，任务队列长度为nteger.MAX_VALUE的线程池，用于执行代理页面下载任务
        proxyDownloadThreadPool = new SimpleThreadPoolExecutor(10, 10,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(), "" +
                "proxyDownloadThreadPool");
        //开启一个新线程用于监视代理测试线程池执行情况
        new Thread(new ThreadPoolMonitor(proxyTestThreadPool, "ProxyTestThreadPool")).start();
        //开启一个新线程用于监视代理页下载线程池执行情况
        new Thread(new ThreadPoolMonitor(proxyDownloadThreadPool, "ProxyDownloadThreadExecutor")).start();
    }

    /**
     * 初始化proxy
     */
    private void initProxy(){
        Proxy[] proxyArray = null;
        try {
            //反序列化代理文件
            proxyArray = (Proxy[]) HttpClientUtil.deserializeObject(Config.proxyPath);
            int usableProxyCount = 0;
            for (Proxy p : proxyArray){
                if (p == null){
                    continue;
                }
                //设置
                p.setTimeInterval(Constants.TIME_INTERVAL);
                p.setFailureTimes(0);
                p.setSuccessfulTimes(0);
                long nowTime = System.currentTimeMillis();
                if (nowTime - p.getLastSuccessfulTime() < 1000 * 60 *60){
                    //上次成功离现在少于一小时
                    ProxyPool.proxyQueue.add(p);
                    ProxyPool.proxySet.add(p);
                    usableProxyCount++;
                }
            }
            logger.info("反序列化proxy成功，" + proxyArray.length + "个代理,可用代理" + usableProxyCount + "个");
        } catch (Exception e) {
            logger.warn("反序列化proxy失败");
        }
    }
    /**
     * 抓取代理
     */
    public void startCrawl(){
        //开启一个新线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    for (String url : ProxyPool.proxyMap.keySet()){
                        /**
                         * 首次本机直接下载代理页面
                         */
                        proxyDownloadThreadPool.execute(new ProxyPageTask(url, false));
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    try {
                        //每隔一小时重新获取代理
                        Thread.sleep(1000 * 60 * 60);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        //创建代理序列化任务
        new Thread(new ProxySerializeTask()).start();
    }
    public ThreadPoolExecutor getProxyTestThreadPool() {
        return proxyTestThreadPool;
    }

    public ThreadPoolExecutor getProxyDownloadThreadPool() {
        return proxyDownloadThreadPool;
    }
}